package plugin.demo.runner.apt.boot.filter;

import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.boot.autoconfigure.AutoConfigurationImportFilter;
import org.springframework.boot.autoconfigure.AutoConfigurationMetadata;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.CollectionUtils;
import plugin.demo.runner.apt.boot.annotations.ElCondition;

import java.util.Arrays;
import java.util.Collection;

/**
 * @Author: Qiao Hang
 * @CreateDate: 2021/5/26 下午5:36
 * @UpdateDate:
 * @Description: 该过滤器用于提供候选组件的筛选能力，针对我们的 @ElCondition注解 / 元数据配置的装载条件 进行候选组件的筛选!
 */
public class ElConfigurationImportFilter implements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware, EnvironmentAware {

    private static final String EL_CONDITION = "ElCondition";
    private BeanFactory beanFactory;
    private ClassLoader classLoader;
    private Environment environment;
    private final ExpressionParser parser = new SpelExpressionParser();

    @Override
    public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
        boolean[] matchResult = checkItemCondition(autoConfigurationClasses, autoConfigurationMetadata);
        return matchResult;
    }

    private boolean[] checkItemCondition(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata){
        boolean[] removeUnits = new boolean[autoConfigurationClasses.length];

        for (int i = 0; i < autoConfigurationClasses.length; i++) {
            if(autoConfigurationClasses[i] == null){
                continue;
            }
            Class autoConfigCls = null;
            try {
                autoConfigCls = classLoader.loadClass(autoConfigurationClasses[i]);
            }catch (Throwable e){
                try {
                    autoConfigCls = Class.forName(autoConfigurationClasses[i]);
                } catch (Throwable ex) {
                    /*Class Not Found, current class will be ignored.*/
                }
            }
            if(autoConfigCls == null){
                removeUnits[i] = true;
            }

            Collection<String> metaDataElConditions = null;
            String metaDataElConditionsStr = autoConfigurationMetadata.get(autoConfigurationClasses[i], EL_CONDITION);
            if(!Strings.isEmpty(metaDataElConditionsStr)){
                metaDataElConditions = Arrays.asList(metaDataElConditionsStr.split(",,"));
            }
            //优先级: 元数据 > 注解
            if(CollectionUtils.isEmpty(metaDataElConditions)){
                metaDataElConditions = touchAnnotationValues(autoConfigCls);
            }
            //存在el表达式时，进行el表达式解析，只要有一条不通过则标记 remove
            if(!CollectionUtils.isEmpty(metaDataElConditions)){
                StandardEvaluationContext elContext = new StandardEvaluationContext();
                elContext.setRootObject(null);
                elContext.setVariable("environment",this.environment);
                elContext.setVariable("classLoader",this.classLoader);
                elContext.setVariable("beanFactory",this.beanFactory);
                for (String metaDataElCondition : metaDataElConditions) {
                    Boolean value = Boolean.FALSE;
                    try {
                        Expression expression = parser.parseExpression(metaDataElCondition);
                        value = expression.getValue(elContext, Boolean.class);
                    }catch (Throwable e){
                        e.printStackTrace();
                        value = Boolean.FALSE;
                    }
                    if(!value.booleanValue()){
                        removeUnits[i] = true;
                        break;
                    }
                }
            }
        }

        boolean[] result = new boolean[removeUnits.length];
        System.arraycopy(removeUnits,0,result,0,removeUnits.length);

        //reverse.
        for (int i = 0; i < result.length; i++) {
            result[i] = !result[i];
        }
        return result;
    }


    private Collection<String> touchAnnotationValues(Class autoConfigCls){
        ElCondition annotationsByType = (ElCondition) autoConfigCls.getDeclaredAnnotation(ElCondition.class);
        if(annotationsByType != null){
            String[] value = annotationsByType.value();
            if(value.length > 0){
                return Arrays.asList(value);
            }
        }
        return null;
    }


    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
}
